package dataImport.action;

import java.util.HashMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import dataImport.action.abstracts.AbstractTempEntityReader;
import dataImport.action.helper.UnityGuardian;
import dataImport.action.manager.EntityReaderManager;
import dataImport.action.manager.storage.EntityReaderStorageKey;
import dataImport.exception.DuplicateAttributeException;
import dataImport.exception.EntityNotFoundException;
import dataImport.exception.MissingContentException;
import dataImport.exception.MissingReader;
import dataImport.exception.NoEntityRelatedWithException;
import dataImport.exception.UniquenessRestrictionViolatedException;
import dataImport.model.Place;
import dataImport.model.Scenario;
import dataImport.model.ScenarioTemp;
import dataImport.model.State;
import dataImport.model.abstracts.AbstractEntity;


public class ScenarioReader extends AbstractTempEntityReader {

	private static final long serialVersionUID = 2763408064714520756L;

	public static final String ENTITY_NAME = "scenario";

	private static final String ATTRIBUTE_STATE_ID = "state";

	private final UnityGuardian stateEntityIdUniqueKey;

	public ScenarioReader() {
		super();
		this.stateEntityIdUniqueKey = new UnityGuardian();
	}

	@Override
	protected void checkRequiredReaders(final EntityReaderManager entityReaderManager) throws MissingReader {
		entityReaderManager.contains(EntityReaderStorageKey.OBJECT);
		entityReaderManager.contains(EntityReaderStorageKey.PLACE);
		entityReaderManager.contains(EntityReaderStorageKey.STATE);
		entityReaderManager.contains(EntityReaderStorageKey.NPC);
	}

	@Override
	protected AbstractEntity createEntity(final Element element) {
		final String id = element.getAttribute(ATTRIBUTE_ID);
		final String stateId = element.getAttribute(ATTRIBUTE_STATE_ID);
		final String text = element.getTextContent();
		final Element parentNode = (Element) element.getParentNode();
		final String parentNodeName = parentNode.getNodeName();
		final String parentNodeId = parentNode.getAttribute(ATTRIBUTE_ID);

		this.verifyRestrictions(id, stateId, text, parentNodeName, parentNodeId);

		if (stateId.isEmpty()) {
			return new ScenarioTemp(id, text, parentNodeId);
		}

		return new ScenarioTemp(id, stateId, text, parentNodeId);
	}

	@Override
	protected void fixEntities(final HashMap<String, AbstractEntity> newEntities, final EntityReaderManager entityReaderManager) throws MissingReader {
		final PlaceReader placeReader = entityReaderManager.getPlaceReader();
		final StateReader stateReader = entityReaderManager.getStateReader();

		for (final AbstractEntity abstractEntity : this.abstractEntities.values()) {
			final ScenarioTemp scenarioTemp = (ScenarioTemp) abstractEntity;

			final String id = scenarioTemp.getId();

			final String text = scenarioTemp.getText();

			State state = null;
			try {
				state = (State) stateReader.getEntityWithId(scenarioTemp.getStateId());
			}
			catch (final EntityNotFoundException e) {}

			final Place place = (Place) placeReader.getEntityWithId(scenarioTemp.getRelatedToId());

			newEntities.put(id, new Scenario(id, state, this.fixText(text, entityReaderManager), place));
		}
	}

	@Override
	public void read(final Document document) {
		this.read(ScenarioReader.ENTITY_NAME, document);
	}

	protected final void verifyRestrictions(final String id, final String stateId, final String text, final String relatedTo, final String relatedToId) {
		super.verifyRestrictions(id);

		if (!relatedTo.equals(PlaceReader.ENTITY_NAME)) {
			throw new NoEntityRelatedWithException(ScenarioReader.ENTITY_NAME, PlaceReader.ENTITY_NAME);
		}

		if (text.isEmpty()) {
			throw new MissingContentException();
		}

		try {
			this.stateEntityIdUniqueKey.ensureUniqueness(stateId, relatedToId);
		}
		catch (final UniquenessRestrictionViolatedException e) {
			throw new DuplicateAttributeException("state", stateId);
		}
	}

	private final String fixText(final String text, final EntityReaderManager entityReaderManager) throws MissingReader {
		String newText = text;

		final PlaceReader placeReader = entityReaderManager.getPlaceReader();
		newText = super.fixText(newText, PlaceReader.ENTITY_NAME, placeReader);

		final ObjectReader objectReader = entityReaderManager.getObjectReader();
		newText = super.fixText(newText, ObjectReader.ENTITY_NAME, objectReader);

		final NpcReader npcReader = entityReaderManager.getNpcReader();
		newText = super.fixText(newText, NpcReader.ENTITY_NAME, npcReader);

		return newText;
	}
}
